/*
import lombok.Data;
import org.springframework.beans.BeanUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

*/
/**
 * @className: AttendanceStatisticByDayDTO
 * @description: 包含 分组字段 和 统计字段
 * @date: 2021/3/14
 * @author: cakin
 *//*

class AttendanceStatisticByDayDTO {
    // 分组字段开始
    */
/**
     * 员工ID
     *//*

    private String userId;
    */
/**
     * 员工编码
     *//*

    private String userCode;
    */
/**
     * 员工名称
     *//*

    private String userName;
    */
/**
     * 考勤配置ID
     *//*

    private String attendanceTypeId;
    */
/**
     * 考勤配置名称
     *//*

    private String attendanceTypeName;
    // 分组字段结束

    // 统计字段开始
    */
/**
     * 应上时长
     *//*

    private Double workDueHour;

    */
/**
     * 实上时长
     *//*

    private Double workRealHour;
    */
/**
     * 统计次数
     *//*

    private Long statisticTimes;
    // 统计字段结束


    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getUserCode() {
        return userCode;
    }

    public void setUserCode(String userCode) {
        this.userCode = userCode;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getAttendanceTypeId() {
        return attendanceTypeId;
    }

    public void setAttendanceTypeId(String attendanceTypeId) {
        this.attendanceTypeId = attendanceTypeId;
    }

    public String getAttendanceTypeName() {
        return attendanceTypeName;
    }

    public void setAttendanceTypeName(String attendanceTypeName) {
        this.attendanceTypeName = attendanceTypeName;
    }

    public Double getWorkDueHour() {
        return workDueHour;
    }

    public void setWorkDueHour(Double workDueHour) {
        this.workDueHour = workDueHour;
    }

    public Double getWorkRealHour() {
        return workRealHour;
    }

    public void setWorkRealHour(Double workRealHour) {
        this.workRealHour = workRealHour;
    }

    public Long getStatisticTimes() {
        return statisticTimes;
    }

    public void setStatisticTimes(Long statisticTimes) {
        this.statisticTimes = statisticTimes;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        AttendanceStatisticByDayDTO that = (AttendanceStatisticByDayDTO) o;
        return Objects.equals(userId, that.userId) &&
                Objects.equals(userCode, that.userCode) &&
                Objects.equals(userName, that.userName) &&
                Objects.equals(attendanceTypeId, that.attendanceTypeId) &&
                Objects.equals(attendanceTypeName, that.attendanceTypeName) &&
                Objects.equals(workDueHour, that.workDueHour) &&
                Objects.equals(workRealHour, that.workRealHour) &&
                Objects.equals(statisticTimes, that.statisticTimes);
    }

    @Override
    public int hashCode() {
        return Objects.hash(userId, userCode, userName, attendanceTypeId, attendanceTypeName, workDueHour, workRealHour, statisticTimes);
    }
}


*/
/**
 * @className: ComputeGroupByDTO
 * @description: 统计字段
 * @date: 2021/3/14
 * @author: cakin
 *//*

//@Data
class ComputeGroupByDTO {
    */
/**
     * 总条数
     *//*

    private Long totalCount;

    */
/**
     * 总的工作小时数
     *//*

    private Double dueHourSum;

    */
/**
     * 真实的小时数
     *//*

    private Double realHourSum;

//    @Override
//    public boolean equals(Object o) {
//        if (this == o) return true;
//        if (o == null || getClass() != o.getClass()) return false;
//        ComputeGroupByDTO that = (ComputeGroupByDTO) o;
//        return Objects.equals(totalCount, that.totalCount) &&
//                Objects.equals(dueHourSum, that.dueHourSum) &&
//                Objects.equals(realHourSum, that.realHourSum);
//    }
//
//    @Override
//    public int hashCode() {
//        return Objects.hash(totalCount, dueHourSum, realHourSum);
//    }

    public Long getTotalCount() {
        return totalCount;
    }

    public void setTotalCount(Long totalCount) {
        this.totalCount = totalCount;
    }

    public Double getDueHourSum() {
        return dueHourSum;
    }

    public void setDueHourSum(Double dueHourSum) {
        this.dueHourSum = dueHourSum;
    }

    public Double getRealHourSum() {
        return realHourSum;
    }

    public void setRealHourSum(Double realHourSum) {
        this.realHourSum = realHourSum;
    }
}

*/
/**
 * @ClassName: GroupSumManyToMany
 * @Description: 多字段分组，多字段求和  参考：https://blog.csdn.net/qq_38428623/article/details/103754446
 * @Date: 2021/3/14
 * @Author: cakin
 *//*

public class GroupSumManyToMany {
    */
/**
     * 功能描述：得到分组字段
     *
     * @param attendanceStatisticByDayDTO 考勤统计
     * @return 返回分组的KEY
     *//*

    private static AttendanceStatisticByDayDTO fetchGroupKey(final AttendanceStatisticByDayDTO attendanceStatisticByDayDTO) {
        final AttendanceStatisticByDayDTO statisticByDay = new AttendanceStatisticByDayDTO();
        statisticByDay.setUserId(attendanceStatisticByDayDTO.getUserId()); // 员工ID
        statisticByDay.setUserCode(attendanceStatisticByDayDTO.getUserCode()); // 员工编码
        statisticByDay.setUserName(attendanceStatisticByDayDTO.getUserName()); // 员工名称
        statisticByDay.setAttendanceTypeId(attendanceStatisticByDayDTO.getAttendanceTypeId()); // 考勤配置ID
        statisticByDay.setAttendanceTypeName(attendanceStatisticByDayDTO.getAttendanceTypeName()); // 考勤配置名称
        return statisticByDay;
    }

    */
/**
     * 功能描述：测试多字段分组，多字段求和
     *
     * @param args 命令行
     * @author cakin
     * @date 2021/3/14
     *//*

    public static void main(final String[] args) {
        final List<AttendanceStatisticByDayDTO> attendanceStatisticByDayDTOS = new ArrayList<>();
        // 初始化数据
        for (long i = 0; i < 100; i++) {
            final AttendanceStatisticByDayDTO attendanceStatisticByDayDTO = new AttendanceStatisticByDayDTO();
            attendanceStatisticByDayDTO.setUserId("" + (i % 5));
            attendanceStatisticByDayDTO.setUserCode("" + (i % 5));
            attendanceStatisticByDayDTO.setUserName("" + (i % 5));
            attendanceStatisticByDayDTO.setWorkDueHour((double) (i % 5));
            attendanceStatisticByDayDTO.setWorkRealHour((double) (i % 5));
            attendanceStatisticByDayDTO.setStatisticTimes(i);
            attendanceStatisticByDayDTO.setAttendanceTypeId("" + (i % 5));
            attendanceStatisticByDayDTO.setAttendanceTypeName("" + (i % 5));
            attendanceStatisticByDayDTOS.add(attendanceStatisticByDayDTO);
        }

        */
/**
         * 获取分组结果
         * 分组字段:员工ID、员工编码、员工名称、考勤配置ID、考勤配置名称,通过 fetchGroupKey 获取
         * 统计字段：通过 collectingAndThen 的第2个参数定义统计方式
         *//*

        final Map<AttendanceStatisticByDayDTO, ComputeGroupByDTO> groupByMap =
                attendanceStatisticByDayDTOS
                        .stream()
                        .collect(Collectors.groupingBy(n -> fetchGroupKey(n), Collectors.collectingAndThen(Collectors.toList(), m -> {
                            // totalCount 采用计数方式
                            final long totalCount = m.stream().count();
                            // dueHourSum 采用求和方式
                            final Double dueHourSum = m.stream().mapToDouble(AttendanceStatisticByDayDTO::getWorkDueHour).sum();
                            // realHourSum 采用求和方式
                            final Double realHourSum = m.stream().mapToDouble(AttendanceStatisticByDayDTO::getWorkDueHour).sum();
                            final ComputeGroupByDTO computeGroupBy = new ComputeGroupByDTO();
                            computeGroupBy.setTotalCount(totalCount); // 计数
                            computeGroupBy.setDueHourSum(dueHourSum); // 求和
                            computeGroupBy.setRealHourSum(realHourSum); // 求和
                            return computeGroupBy;
                        })));
        System.out.println("===============结果大小===============================");
        System.out.println("groupByMap=" + groupByMap.size());

        List<AttendanceStatisticByDayDTO> list = new ArrayList<>();
        System.out.println("===============中间结果===============================");
        groupByMap.forEach((k, v) -> {
            System.out.println(k);
            System.out.println(v);
            AttendanceStatisticByDayDTO attendanceStatisticByDayDTO = new AttendanceStatisticByDayDTO();
            BeanUtils.copyProperties(k, attendanceStatisticByDayDTO);
            attendanceStatisticByDayDTO.setStatisticTimes(v.getTotalCount());
            attendanceStatisticByDayDTO.setWorkDueHour(v.getDueHourSum());
            attendanceStatisticByDayDTO.setWorkRealHour(v.getRealHourSum());
            list.add(attendanceStatisticByDayDTO);
        });

        System.out.println("===============最终结果===============================");
        for (AttendanceStatisticByDayDTO attendanceStatisticByDayDTO : list) {
            System.out.println(attendanceStatisticByDayDTO);
        }
    }
}
*/
